package logger

import (
	"net"
	"net/http"
	"net/http/httputil"
	"os"
	"runtime/debug"
	"strings"
	"time"
	"tinyGW/pkg/service/conf"

	"github.com/gin-gonic/gin"
	"github.com/gookit/color"
	"github.com/natefinch/lumberjack"
	"go.uber.org/fx"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

var log *zap.Logger

func S() *zap.Logger {
	return log
}

// filterCore 自定义Core，用于过滤特定的日志
type filterCore struct {
	zapcore.Core
}

// newFilterCore 创建一个新的过滤Core
func newFilterCore(core zapcore.Core) zapcore.Core {
	return &filterCore{
		Core: core,
	}
}

// Check 检查日志条目是否应该被记录
func (c *filterCore) Check(entry zapcore.Entry, checkedEntry *zapcore.CheckedEntry) *zapcore.CheckedEntry {
	// 过滤fx框架的非错误日志 - 更精确的匹配模式
	if (strings.Contains(entry.LoggerName, "fx") ||
		strings.Contains(entry.Caller.File, "fxevent") ||
		strings.Contains(entry.Caller.File, "go.uber.org/fx") ||
		strings.Contains(entry.Message, "provided") ||
		strings.Contains(entry.Message, "supplied") ||
		strings.Contains(entry.Message, "decorated") ||
		strings.Contains(entry.Message, "invoking") ||
		strings.Contains(entry.Message, "started") ||
		strings.Contains(entry.Message, "stopped")) &&
		entry.Level < zapcore.ErrorLevel {
		return checkedEntry
	}

	return c.Core.Check(entry, checkedEntry)
}

// Write 写入日志条目（添加额外的过滤逻辑）
func (c *filterCore) Write(entry zapcore.Entry, fields []zapcore.Field) error {
	// 再次检查，确保fx的非错误日志不被写入
	if (strings.Contains(entry.LoggerName, "fx") ||
		strings.Contains(entry.Caller.File, "fxevent") ||
		strings.Contains(entry.Caller.File, "go.uber.org/fx") ||
		strings.Contains(entry.Message, "provided") ||
		strings.Contains(entry.Message, "supplied") ||
		strings.Contains(entry.Message, "decorated") ||
		strings.Contains(entry.Message, "invoking") ||
		strings.Contains(entry.Message, "started") ||
		strings.Contains(entry.Message, "stopped")) &&
		entry.Level < zapcore.ErrorLevel {
		return nil
	}

	return c.Core.Write(entry, fields)
}

// colorLevelEncoder 为不同级别的日志添加颜色
func colorLevelEncoder(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
	var levelStr string
	switch level {
	case zapcore.DebugLevel:
		levelStr = color.Cyan.Sprintf("%-5s", "DEBUG")
	case zapcore.InfoLevel:
		levelStr = color.Green.Sprintf("%-5s", "INFO")
	case zapcore.WarnLevel:
		levelStr = color.Yellow.Sprintf("%-5s", "WARN")
	case zapcore.ErrorLevel:
		levelStr = color.Red.Sprintf("%-5s", "ERROR")
	case zapcore.DPanicLevel:
		levelStr = color.Magenta.Sprintf("%-5s", "DPANIC")
	case zapcore.PanicLevel:
		levelStr = color.Magenta.Sprintf("%-5s", "PANIC")
	case zapcore.FatalLevel:
		levelStr = color.Red.Sprintf("%-5s", "FATAL")
	default:
		levelStr = color.White.Sprintf("%-5s", level.CapitalString())
	}
	enc.AppendString(levelStr)
}

// colorTimeEncoder 为时间添加颜色
func colorTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
	timeStr := color.Blue.Sprint(t.Format("2006-01-02 15:04:05.000"))
	enc.AppendString(timeStr)
}

// colorCallerEncoder 为调用者信息添加颜色
func colorCallerEncoder(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) {
	callerStr := color.Gray.Sprint(caller.TrimmedPath())
	enc.AppendString(callerStr)
}

// colorNameEncoder 为logger名称添加颜色
func colorNameEncoder(loggerName string, enc zapcore.PrimitiveArrayEncoder) {
	nameStr := color.Magenta.Sprintf("[%s]", loggerName)
	enc.AppendString(nameStr)
}

// colorMessageEncoder 为消息添加颜色（根据日志级别）
func colorMessageEncoder(msg string, enc zapcore.PrimitiveArrayEncoder) {
	// 这里我们无法直接获取级别，所以使用默认颜色
	messageStr := color.White.Sprint(msg)
	enc.AppendString(messageStr)
}

func NewLogger(config *conf.Config) *zap.Logger {
	writeSyncer := getLogWriter(config.Logger.Filename, config.Logger.MaxSize, config.Logger.MaxBackups, config.Logger.MaxAge)
	level := getLevel(config.Logger.Level)

	// 控制台编码器（带颜色）
	consoleEncoder := getColorEncoder()
	// 文件编码器（不带颜色）
	fileEncoder := getEncoder()

	// 创建基础Core
	consoleCore := zapcore.NewCore(consoleEncoder, zapcore.AddSync(os.Stdout), level)
	fileCore := zapcore.NewCore(fileEncoder, zapcore.AddSync(writeSyncer), level)

	// 包装为过滤Core
	filteredConsoleCore := newFilterCore(consoleCore)
	filteredFileCore := newFilterCore(fileCore)

	core := zapcore.NewTee(filteredConsoleCore, filteredFileCore)

	log = zap.New(core, zap.AddCaller())
	zap.ReplaceGlobals(log)
	return log
}

func getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {
	lumberJackLogger := &lumberjack.Logger{
		Filename:   filename,
		MaxSize:    maxSize,
		MaxBackups: maxBackup,
		MaxAge:     maxAge,
		Compress:   true,
	}
	return zapcore.AddSync(lumberJackLogger)
}

func getLevel(level string) zapcore.Level {
	switch level {
	case "debug":
		return zap.DebugLevel
	case "info":
		return zap.InfoLevel
	case "error":
		return zap.ErrorLevel
	default:
		return zap.InfoLevel
	}
}

func getEncoder() zapcore.Encoder {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder

	return zapcore.NewConsoleEncoder(encoderConfig)
}

// getColorEncoder 获取带颜色的编码器
func getColorEncoder() zapcore.Encoder {
	encoderConfig := zap.NewDevelopmentEncoderConfig()
	encoderConfig.EncodeTime = colorTimeEncoder
	encoderConfig.EncodeLevel = colorLevelEncoder
	encoderConfig.EncodeCaller = colorCallerEncoder
	encoderConfig.EncodeName = colorNameEncoder

	// 使用更美观的分隔符
	encoderConfig.ConsoleSeparator = " | "

	// 添加调用堆栈键
	encoderConfig.StacktraceKey = "stacktrace"

	return zapcore.NewConsoleEncoder(encoderConfig)
}

// GinLogger 接收gin框架默认的日志
func GinLogger() gin.HandlerFunc {
	return func(c *gin.Context) {
		start := time.Now()
		path := c.Request.URL.Path
		query := c.Request.URL.RawQuery
		c.Next()

		cost := time.Since(start)
		zap.L().Info(path,
			zap.Int("status", c.Writer.Status()),
			zap.String("method", c.Request.Method),
			zap.String("path", path),
			zap.String("query", query),
			zap.String("ip", c.ClientIP()),
			zap.String("user-agent", c.Request.UserAgent()),
			zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
			zap.Duration("cost", cost),
		)
	}
}

// GinRecovery recover掉项目可能出现的panic，并使用zap记录相关日志
func GinRecovery(stack bool) gin.HandlerFunc {
	return func(c *gin.Context) {
		defer func() {
			if err := recover(); err != any(nil) {
				// Check for a broken connection, as it is not really a
				// condition that warrants a panic stack trace.
				var brokenPipe bool
				var errWrapper interface{} = err
				if ne, ok := errWrapper.(*net.OpError); ok {
					if se, ok := ne.Err.(*os.SyscallError); ok {
						if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
							brokenPipe = true
						}
					}
				}

				httpRequest, _ := httputil.DumpRequest(c.Request, false)
				if brokenPipe {
					zap.L().Error(c.Request.URL.Path,
						zap.Any("error", err),
						zap.String("request", string(httpRequest)),
					)
					// If the connection is dead, we can't write a status to it.
					var errWrapper interface{} = err
					c.Error(errWrapper.(error)) // nolint: errcheck
					c.Abort()
					return
				}

				if stack {
					zap.L().Error("[Recovery from panic]",
						zap.Any("error", err),
						zap.String("request", string(httpRequest)),
						zap.String("stack", string(debug.Stack())),
					)
				} else {
					zap.L().Error("[Recovery from panic]",
						zap.Any("error", err),
						zap.String("request", string(httpRequest)),
					)
				}
				c.AbortWithStatus(http.StatusInternalServerError)
			}
		}()
		c.Next()
	}
}

var Module = fx.Provide(
	NewLogger,
)
